package com.ruoyi.wechat.utils;

import com.ruoyi.wechat.common.ServiceException;
import com.wechat.pay.java.core.util.PemUtil;
import okhttp3.HttpUrl;
import org.apache.http.HttpStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;

/**
 * <p>
 * 微信小程序下单签名
 * </p>
 *
 * @author YinHeng
 * @since 2024/3/5
 */
@Component
public class WXPayUtil {

    private static String appId;

    private static String appSecret;

    private static String mchId;

    private static String mchSerialNo;

    private static String apiKey;

    private static String notifyUrl;

    private static String keyPath;

    @Autowired
    public void init(@Value("${wx.app-id}") String appId,
                     @Value("${wx.app-secret}") String appSecret,
                     @Value("${wx.mch-id}") String mchId,
                     @Value("${wx.mch-serial-no}") String mchSerialNo,
                     @Value("${wx.api-key}") String apiKey,
                     @Value("${wx.notify-url}") String notifyUrl,
                     @Value("${wx.key-path}") String keyPath) {
        WXPayUtil.appId = appId;
        WXPayUtil.appSecret = appSecret;
        WXPayUtil.mchId = mchId;
        WXPayUtil.mchSerialNo = mchSerialNo;
        WXPayUtil.apiKey = apiKey;
        WXPayUtil.notifyUrl = notifyUrl;
        WXPayUtil.keyPath = keyPath;
    }


    /**
     * 获取签名
     *
     * @param signatureStr
     * @param privateKey
     * @return
     * @throws InvalidKeyException
     * @throws NoSuchAlgorithmException
     * @throws SignatureException
     * @throws IOException
     * @throws URISyntaxException
     */
    public static String getSign(String signatureStr, String privateKey) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, IOException, URISyntaxException {
        //replace 根据实际情况，不一定都需要
        String replace = privateKey.replace("\\n", "\n");
        PrivateKey merchantPrivateKey = PemUtil.loadPrivateKeyFromPath(replace);
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(merchantPrivateKey);
        sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
        return Base64Utils.encodeToString(sign.sign());
    }

    /**
     * 获取随机数
     *
     * @param length
     * @return
     */
    public static String genRandomHex(int length) {
        SecureRandom secureRandom = new SecureRandom();
        byte[] randomBytes = new byte[length];
        secureRandom.nextBytes(randomBytes);

        StringBuilder hexString = new StringBuilder();
        for (byte b : randomBytes) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex);
        }

        return hexString.toString().toUpperCase();
    }

    /**
     * 构造请求头认证信息
     * @param method
     * @param url
     * @param body
     * @return
     * @throws UnsupportedEncodingException
     * @throws NoSuchAlgorithmException
     * @throws SignatureException
     * @throws InvalidKeyException
     */
    public static String getToken(String method, String url, String body) throws UnsupportedEncodingException, NoSuchAlgorithmException, SignatureException, InvalidKeyException {
        HttpUrl httpurl = HttpUrl.parse(url);
        String nonceStr = genRandomHex(16);
        long timestamp = System.currentTimeMillis() / 1000;
        String message = buildMessage(method, httpurl, timestamp, nonceStr, body);
        String signature = sign(message.getBytes("utf-8"));

        return "WECHATPAY2-SHA256-RSA2048 mchid=\"" + mchId + "\","
                + "nonce_str=\"" + nonceStr + "\","
                + "timestamp=\"" + timestamp + "\","
                + "serial_no=\"" + mchSerialNo + "\","
                + "signature=\"" + signature + "\"";
    }

    /**
     * 微信支付数据签名
     * @param message
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     * @throws SignatureException
     */
    static String sign(byte[] message) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        Signature sign = Signature.getInstance("SHA256withRSA");
        String replace = keyPath.replace("\\n", "\n");
        PrivateKey merchantPrivateKey;
        try {
            merchantPrivateKey = PemUtil.loadPrivateKeyFromPath(replace);
        } catch (Exception e) {
            throw new ServiceException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "证书加载异常");
        }
        sign.initSign(merchantPrivateKey);
        sign.update(message);

        return Base64.getEncoder().encodeToString(sign.sign());
    }

    /**
     * 签名数据构建
     * @param method
     * @param url
     * @param timestamp
     * @param nonceStr
     * @param body
     * @return
     */
    static String  buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) {
        String canonicalUrl = url.encodedPath();
        if (url.encodedQuery() != null) {
            canonicalUrl += "?" + url.encodedQuery();
        }

        return method + "\n"
                + canonicalUrl + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + body + "\n";
    }


}